<?php
/**
 * Project: AIGC
 * Author: moufer <moufer@163.com>
 * Date: 2024/7/20 22:35
 */

namespace module\chat\controller;

use module\ai\library\channel\exception\ChatException;
use module\chat\front\input\MessageInput;
use module\chat\front\searcher\MessageSearcher;
use module\chat\logic\MessageLogic;
use module\user\library\UserController;
use mof\ApiResponse;
use mof\exception\LogicException;
use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\TransporterException;
use think\App;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;

class Message extends UserController
{

    public function __construct(App $app, protected MessageLogic $logic)
    {
        parent::__construct($app);
        $this->logic->withUser($this->auth->getUser());
    }

    /**
     * 清除上下文
     * @param int $id
     * @return Json
     * @throws DataNotFoundException
     */
    public function clearContext(int $id): Json
    {
        $this->logic->clearContext($id);
        return ApiResponse::success();
    }

    public function delete(int $id): Json
    {
        $this->logic->withAccess(['user_id' => $this->auth->getId()])->delete($id);
        return ApiResponse::success();
    }

    /**
     * 删除当前用户里的指定数据
     * @return Json
     */
    public function deleteAll(): Json
    {
        $params = $this->request
            ->withValidate([
                'object_type' => 'require|in:visrole,assistant,unlimited',
                'object_id'   => 'require|integer',
                'channel_id'  => 'require|integer'
            ])->get(['object_type', 'object_id', 'channel_id']);

        //过滤空内容的参数
        $params = array_filter($params);
        $this->logic->deleteAll($params);
        return ApiResponse::success();
    }

    /**
     * 提交用户输入
     * @param MessageInput $message
     * @return Json
     * @throws DbException
     */
    public function send(MessageInput $message): Json
    {
        $model = $this->logic->save($message->data() + ['user_id' => $this->auth->getId()]);
        $model->append(['channel'])->scene('prompt');
        return ApiResponse::success($model->refresh());
    }

    /**
     * 接收返回消息
     * @param int $id 消息ID
     * @return Json
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function receive(int $id): Json
    {
        try {
            $completion = $this->logic->completion($id);
            return ApiResponse::success($completion->sse(false));
        } catch (ChatException $e) {
            return ApiResponse::error($e->getMessage());
        }
    }

    public function receiveStream(int $id): void
    {
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache');
        header('Connection: keep-alive');
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Credentials: true');
        header('X-Accel-Buffering: no');
        try {
            try {
                $this->logic->completionStream($id, function ($chunk) {
                    echo "data: " . $chunk . "\n\n";
                    ob_flush();
                    flush();
                    usleep(30000); // 控制发送速度 毫秒
                });
            } catch (LogicException|ErrorException $e) {
                throw new ChatException($e->getMessage(), $e->getCode() ?: 1);
            } catch (TransporterException $e) {
                $message = env('app_debug', false) ? $e->getMessage() : '无法访问，请稍后尝试';
                throw new ChatException($message, $e->getCode() ?: 1);
            }
        } catch (ChatException $e) {
            echo "data: " . $e->see() . "\n\n";
            ob_flush();
            flush();
        }
    }

    public function cancel(int $id): Json
    {
        $this->logic->cancelReceive($id);
        return ApiResponse::success();
    }

    /**
     * 对话记录列表
     * @param MessageSearcher $searcher
     * @return Json
     */
    public function history(MessageSearcher $searcher): Json
    {
        return ApiResponse::success($this->logic->histories($searcher));
    }

}